﻿using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Windows.Forms;
using FC.GEPluginCtrls;
using FC.GEPluginCtrls.Geo;
using FC.GEPluginCtrls.HttpServer;
using System.Threading;
using System.IO;
using System.Text.RegularExpressions;

using System.Linq;

using VehicleTrajectoryApp;


namespace WindowsFormsApplication1
{
    public partial class MainForm : Form
    {
        int eventCounter = 0;

        private dynamic ge = null;

        private GEServer server = null;
        //private Network network = new Network();

        //private Coordinate StartPoint;
        //private Coordinate EndPoint;
        //private bool firstClick = true;

        //private string pathStr;

        private List<DYNASMARTVehicle> vehicleList;
        public Dictionary<int, VehicleTrajectoryApp.Node> nodeMap;

        //private bool IsEventHandlerAdded = false;

        private List<string> ScenarioNameList = new List<string>();

        private Dictionary<int, int> BPMZoneIDToDSPZoneIDMap = new Dictionary<int, int>();

        private Dictionary<int, int> DYNASMARTZoneIDToBPMZoneID;

        Dictionary<ulong, CriticalOD> criticalODMap = new Dictionary<ulong, CriticalOD>();
        Dictionary<ulong, int> criticalODCounts = new Dictionary<ulong, int>();

        //Dictionary<int, int> NodeZoneMap = null;

        public string projectFolder = "";

        private List<dynamic> previousKMLObjList = null;

        private dynamic ODKMLObj = null;
        private dynamic potentialDestinationKMLObj = null;
        private dynamic StartPointKMLObj = null;
        private dynamic EndPointKMLObj = null;

        private bool SelectOriginZone = false;
        private bool SelectDestZone = false;

        private int SelectedOriginZoneID = -1;
        private int SelectedDestZoneID = -1;
        private Coordinate SelectedOriginCoordinate = null;
        private Coordinate SelectedDestCoordinate = null;

        List<IVehicle> gpsTrajectoryList;

        private int NumOfScenarios = 0;
        private string[] ScenariosFileNames = null;
        private VehicleTrajectoryType[] ScenarioTypeArray = null;
        private double[] ScenarioWeights = null;
        private bool bReadGPSTrajectory = false;
        private string GPSTrajectoryFileName = "";
        private bool bLoadNetworkKMLFile = false;
        private string NetworkKMLFileName = "";
        //private List<DYNASMARTVehicle>[] ScenarioSpecificVehicleList;
        private int Analysis_Start_Time = 6;
        private int Analysis_End_Time = 12;

        public Dictionary<ulong, Dictionary<int, List<IVehicle>>> gpsVehicles;
        public Dictionary<ulong, Dictionary<int, List<IVehicle>>> aimsunVehicles;

        //private Dictionary<int, List<VehicleTrajectoryApp.Node>> destinationZoneMap;
        private Dictionary<int, List<int>> TAZZoneIDToNodeIDListMap;

        public MainForm()
        {
            InitializeComponent();

            //GETraceListener trace = new GETraceListener(richTextBox1);
            //richTextBox1.LinkClicked += (o, e) => { Process.Start(e.LinkText); };

            //Debug.Listeners.Add(trace);

            server = new GEServer(System.Net.IPAddress.Loopback, 8080, "webroot");
            server.Start();

            this.geWebBrowser1.PluginReady += geWebBrowser1_PluginReady;
            this.geWebBrowser1.KmlLoaded += geWebBrowser1_KmlLoaded;
            this.geWebBrowser1.LoadEmbeddedPlugin();
            this.geWebBrowser1.ScriptError += (o, ea) =>
            {
                Debug.WriteLine(ea.Message + ": " + ea.Data, "Script-Error");
            };

            this.geToolStrip1.refreshButton.Click += new EventHandler(refreshButton_Click);
        }

        void refreshButton_Click(object sender, EventArgs e)
        {
            if (previousKMLObjList != null && previousKMLObjList.Count > 0)
            {
                //for (int i = 0; i < previousKMLObjList.Count; i++)
                //{
                //    ge.getFeatures().removeChild(previousKMLObjList[i]);
                //    previousKMLObjList[i] = null;
                //}

                previousKMLObjList.Clear();
            }
        }

        void geWebBrowser1_PluginReady(object sender, GEEventArgs e)
        {
            this.ge = e.ApiObject;          

            this.geToolStrip1.SetBrowserInstance(this.geWebBrowser1);
            this.geStatusStrip1.SetBrowserInstance(this.geWebBrowser1);
            this.KmlTreeView1.SetBrowserInstance(this.geWebBrowser1);

            // run the tests
            //LoadKmlExamples();
            //TestEvents();
            //TestCoordinate();
            //TestMaths();

            ge.getNavigationControl().setVisibility(ge.VISIBILITY_SHOW);
        }

        void geWebBrowser1_KmlLoaded(object sender, GEEventArgs e)
        {
            var kml = e.ApiObject;
            KmlTreeView1.ParseKmlObject(kml);
            geWebBrowser1.ParseKmlObject(kml);
            geWebBrowser1.AddEventListener(kml, EventId.Click);
            geWebBrowser1.KmlEvent += geWebBrowser1_KMLEvent;
        }

        private void ReadZoneInfoFile(string zoneInfoFileName)
        {
            if (File.Exists(zoneInfoFileName))
            {
                using (StreamReader reader = new StreamReader(zoneInfoFileName))
                {
                    string line = reader.ReadLine();
                    int commaIndex = line.IndexOf(',');
                    int BPMIndex = line.ToUpper().IndexOf("BPM");
                    bool isFirstColumn = true;
                    if (BPMIndex < commaIndex)
                    {
                        isFirstColumn = false;
                    }
                    DYNASMARTZoneIDToBPMZoneID = new Dictionary<int, int>();
                    int DYNASMARTZoneID, BPMZoneID;
                    while ((line = reader.ReadLine()) != null)
                    {
                        string[] subString = line.Split(',');
                        if (isFirstColumn)
                        {
                            DYNASMARTZoneID = Int32.Parse(subString[0]);
                            BPMZoneID = Int32.Parse(subString[1]);
                        }
                        else
                        {
                            DYNASMARTZoneID = Int32.Parse(subString[1]);
                            BPMZoneID = Int32.Parse(subString[0]);
                        }

                        DYNASMARTZoneIDToBPMZoneID[DYNASMARTZoneID] = BPMZoneID;
                    }
                }
            }
        }

        //private void ReadDestinationFile(string destinationFileName)
        //{
        //    if (File.Exists(destinationFileName))
        //    {
        //        using (StreamReader reader = new StreamReader(destinationFileName))
        //        {
        //            string line;

        //            while ((line = reader.ReadLine()) != null)
        //            {
        //                MatchCollection matches = Regex.Matches(line, @"[0-9.]+");

        //                List<int> values = new List<int>();
        //                foreach (Match match in matches)
        //                {
        //                    values.Add(int.Parse(match.Groups[0].Value));
        //                }


        //                int destination_node = values[0];
        //                if (destinationZoneMap == null)
        //                {
        //                    destinationZoneMap = new Dictionary<int, List<VehicleTrajectoryApp.Node>>();
        //                }
        //                destinationZoneMap[destination_node] = new List<VehicleTrajectoryApp.Node>();
        //                int numOfNodes = values[1];
        //                for (int i = 0; i < numOfNodes; i++)
        //                {
        //                    if (nodeMap.ContainsKey(values[2 + i]))
        //                    {
        //                        destinationZoneMap[destination_node].Add(nodeMap[values[2 + i]]);
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        private int MapFromCoordinateToZoneID(Coordinate coor, int specified_origin_zone_id = -1)
        {
            int zone_id = -1;
            double minDistance = double.MaxValue;
            List<int> potentialDestZoneIDList = new List<int>();

            // origin_zone_id known, to find the destination node and return its TAZ ID
            if (specified_origin_zone_id != -1 && criticalODMap != null) 
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                int origin_zone_id;
                int dest_zone_id;

                //find all potential OD pairs with the specified origin_zone_id
                for (int i = 0; i < keyList.Count; i++)
                {
                    origin_zone_id = (int)((keyList[i] & 0xffffffff00000000) >> 32);
                    dest_zone_id = (int)(keyList[i] & 0x00000000ffffffff);

                    if (origin_zone_id == specified_origin_zone_id && origin_zone_id != dest_zone_id)
                    {
                        potentialDestZoneIDList.Add(dest_zone_id);
                    }
                }

                //find the closest node and return its TAZ ID
                if (potentialDestZoneIDList.Count > 0)
                {
                    foreach (int dzone in potentialDestZoneIDList)
                    {
                        List<int> nodeIDList = TAZZoneIDToNodeIDListMap[dzone];
                        foreach (int node_id in nodeIDList)
                        {
                            Node node = nodeMap[node_id];
                            double distance = Maths.DistanceCosine(coor, new Coordinate(node.Latitude, node.Longitude));
                            if (distance < minDistance)
                            {
                                minDistance = distance;
                                zone_id = node.TAZ_ID;
                            }
                        }
                    }
                }
            }
            else // with the specified coordinate, find the closest node and return the TAZ ID (origin node)
            {
                if (nodeMap != null)
                {
                    //find the closest node and return its TAZ ID
                    foreach (KeyValuePair<int, VehicleTrajectoryApp.Node> kvp in nodeMap)
                    {
                        VehicleTrajectoryApp.Node node = kvp.Value;
                        double distance = Maths.DistanceCosine(coor, new Coordinate(node.Latitude, node.Longitude));
                        if (distance < minDistance)
                        {
                            minDistance = distance;
                            zone_id = node.TAZ_ID;
                        }
                    }
                }
            }
            return zone_id;
        }


        private string GeneratePointKmlString(Coordinate coordinate, bool isStart)
        {
            string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
            s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
            s += "<Document>\n";
            s += "<Style id=\"My_Style\">";
            s += "<IconStyle> <Icon><href>";
            string url = "";
            if (isStart)
            {
                url = "http://maps.google.com/mapfiles/kml/paddle/O.png";
            }
            else
            {
                url = "http://maps.google.com/mapfiles/kml/paddle/D.png";
            }
            s += url;
            s += "</href> </Icon></IconStyle></Style>\n";
            
            s += "<Placemark>\n";
            s += "<Name>";
            s += isStart ? "Origin Zone" : "Destination Zone";
            s += "</Name>\n";
            s += "<styleUrl> #My_Style</styleUrl>\n";
            s += "<Point>\n";

            s += "<coordinates>\n";

            s += String.Format("{0},{1},0", coordinate.Longitude, coordinate.Latitude);

            s += "</coordinates>\n";
            s += "</Point>\n";
            s += "</Placemark>\n";
            s += "</Document>";
            s += "</kml>";

            return s;
        }
        private string GenerateDestinationZoneKmlString(int specified_origin_zone_id)
        {

            List<int> potentialDestZoneIDList = new List<int>();

            if (criticalODMap != null)
            {
                List<ulong> keyList = criticalODMap.Keys.ToList();
                int origin_zone_id;
                int dest_zone_id;

                for (int i = 0; i < keyList.Count; i++)
                {
                    origin_zone_id = (int)((keyList[i] & 0xffffffff00000000) >> 32);
                    dest_zone_id = (int)(keyList[i] & 0x00000000ffffffff);

                    if (origin_zone_id == specified_origin_zone_id && origin_zone_id != dest_zone_id)
                    {
                        potentialDestZoneIDList.Add(dest_zone_id);
                        //if (criticalODMap[keyList[i]].totalCounts > 20)
                        //{
                        //    potentialDestZoneIDList.Add(dest_zone_id);
                        //}
                    }
                }
            }

            if (potentialDestZoneIDList.Count > 0)
            {
                string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
                s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
                s += "<Document>";               

                foreach (int dest_zone_id in potentialDestZoneIDList)
                {
                    List<int> nodeIDList = TAZZoneIDToNodeIDListMap[dest_zone_id];

                    for (int n = 0; n < nodeIDList.Count; n++)
                    {
                        s += "<Placemark>\n";
                        s += "<Point>\n";

                        s += "<coordinates>\n";

                        s += String.Format("{0},{1}", nodeMap[nodeIDList[n]].Longitude, nodeMap[nodeIDList[n]].Latitude);

                        s += "</coordinates>\n";
                        s += "</Point>\n";
                        s += "</Placemark>\n";
                    }
                }

                s += "</Document>";
                s += "</kml>";

                return s;
            }
            else
            {
                return "";
            }
        }

        private string GenerateODLineKmlString(Coordinate startCoordinate, Coordinate endCoordinate)
        {
            string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
            s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
            s += "<Document>";

            s += "<Placemark>\n";
            s += "<Style><LineStyle><width>4</width><color>ff00ff00</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>\n";
            s += "<LineString>\n";
            s += "<extrude>0</extrude>\n";
            s += "<tessellate>0</tessellate>\n";
            s += "<altitudeMode>relativeToGround</altitudeMode>\n";
            s += "<coordinates>\n";

            s += String.Format("{0},{1},0 {2},{3},0\n", startCoordinate.Longitude, startCoordinate.Latitude, endCoordinate.Longitude, endCoordinate.Latitude);

            s += "</coordinates>\n";
            s += "</LineString>\n";
            s += "</Placemark>\n";
            s += "</Document>";
            s += "</kml>";
            return s;
        }

        void geWebBrowser1_KMLEvent(object sender, GEEventArgs e)
        {
            dynamic kmlMouseEvent = e.ApiObject;

            //disable balloon
            kmlMouseEvent.preventDefault();

            double lat = kmlMouseEvent.getLatitude();
            double lng = kmlMouseEvent.getLongitude();
            double alt = kmlMouseEvent.getAltitude();

            if (SelectOriginZone)
            {
                SelectOriginZone = false;
                SelectedOriginCoordinate = new Coordinate(lat, lng);

                string kmlString = GeneratePointKmlString(SelectedOriginCoordinate, true);

                if (StartPointKMLObj != null)
                {
                    ge.getFeatures().removeChild(StartPointKMLObj);
                    StartPointKMLObj = null;
                }

                StartPointKMLObj = this.geWebBrowser1.ParseKml(kmlString);

                if (StartPointKMLObj != null)
                {
                    geWebBrowser1.ParseKmlObject(StartPointKMLObj);
                }
                
                SelectedOriginZoneID = MapFromCoordinateToZoneID(SelectedOriginCoordinate);
                
                kmlString = GenerateDestinationZoneKmlString(SelectedOriginZoneID);

                if (potentialDestinationKMLObj != null)
                {
                    ge.getFeatures().removeChild(potentialDestinationKMLObj);
                    potentialDestinationKMLObj = null;
                }

                potentialDestinationKMLObj = this.geWebBrowser1.ParseKml(kmlString);

                if (potentialDestinationKMLObj != null)
                {
                    geWebBrowser1.ParseKmlObject(potentialDestinationKMLObj);
                }
            }

            if (SelectDestZone)
            {
                SelectDestZone = false;
                SelectedDestCoordinate = new Coordinate(lat, lng);

                string pointKmlString = GeneratePointKmlString(SelectedDestCoordinate, false);

                if (EndPointKMLObj != null)
                {
                    ge.getFeatures().removeChild(EndPointKMLObj);
                    StartPointKMLObj = null;
                }

                EndPointKMLObj = this.geWebBrowser1.ParseKml(pointKmlString);

                if (EndPointKMLObj != null)
                {
                    geWebBrowser1.ParseKmlObject(EndPointKMLObj);
                }

                if (potentialDestinationKMLObj != null)
                {
                    ge.getFeatures().removeChild(potentialDestinationKMLObj);
                    potentialDestinationKMLObj = null;
                }
                
                SelectedDestZoneID = MapFromCoordinateToZoneID(SelectedDestCoordinate, SelectedOriginZoneID);

                if (SelectedDestZoneID != -1 && SelectedOriginZoneID != -1)
                {
                    string kmlString = GenerateODLineKmlString(SelectedOriginCoordinate, SelectedDestCoordinate);
                    if (ODKMLObj != null)
                    {
                        ge.getFeatures().removeChild(ODKMLObj);
                        ODKMLObj = null;
                    }
                    ODKMLObj = this.geWebBrowser1.ParseKml(kmlString);

                    if (ODKMLObj != null)
                    {
                        geWebBrowser1.ParseKmlObject(ODKMLObj);
                    }

                    ODTravelTimeStatistics form = new ODTravelTimeStatistics();
                    form.criticalODMap = criticalODMap;
                    form.parentForm = this;
                    form.NumOfScenarios = this.NumOfScenarios;
                    form.ScenarioNames = new string[this.NumOfScenarios];
                    form.ScenarioWeights = this.ScenarioWeights;
                    form.AnalysisStartTime = this.Analysis_Start_Time;
                    form.AnalysisEndTime = this.Analysis_End_Time;
                    form.gpsTrajectoryList = this.gpsTrajectoryList;
                    form.gpsVehicles = this.gpsVehicles;
                    for (int n = 0; n < this.NumOfScenarios; n++)
                    {
                        form.ScenarioNames[n] = System.IO.Path.GetFileNameWithoutExtension(this.ScenariosFileNames[n]);
                    }
                    form.SpecifyOriginAndDestination(SelectedOriginZoneID, SelectedDestZoneID);
                    form.Show();
                }
            }
        }

        void geWebBrowser1_PluginEvent(object sender, GEEventArgs e)
        {
            if (eventCounter <= 10)
            {
                if (e.EventId == EventId.BalloonClose)
                {
                    Debug.WriteLine("balloon closed");

                }
                else if (e.EventId == EventId.BalloonOpening)
                {
                    Debug.WriteLine("balloon opening");
                }

                eventCounter += 1;
            }
            else
            {
                geWebBrowser1.RemoveEventListener(ge, EventId.BalloonOpening);
                geWebBrowser1.RemoveEventListener(ge, EventId.BalloonClose);
                geWebBrowser1.PluginEvent -= geWebBrowser1_PluginEvent; // unsubscribe
            }
        }

        private void toolStripMenuItemClear_Click(object sender, EventArgs e)
        {
            //this.richTextBox1.Clear();
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            openFileDialog1.FileName = "";
            openFileDialog1.Filter = "KML (*.kml) | *.kml";
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                string srcFileName = openFileDialog1.FileName;
                string destFileName = System.IO.Path.Combine(Application.StartupPath, GEServer.RootDirectory, System.IO.Path.GetFileName(srcFileName));
                if (File.Exists(destFileName))
                {
                    System.IO.File.Delete(destFileName);
                }
                System.IO.File.Copy(srcFileName, destFileName);

                string kmlURL = String.Format("http://localhost:8080/{0}", System.IO.Path.GetFileName(srcFileName));

                this.geWebBrowser1.FetchKml(kmlURL);
            }
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            this.geWebBrowser1.FetchKml("http://localhost:8080/SP.kml");
            this.geWebBrowser1.FetchKml("http://localhost:8080/LinkMOE.kml");
            System.Windows.Forms.MessageBox.Show("Link MOEs loaded!");
        }

        private void toolStripButton4_Click(object sender, EventArgs e)
        {
            openFileDialog1.FileName = "";
            openFileDialog1.Filter = "Vehicle Trajectory Project File (*.ini) | *.ini";
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                
                this.projectFolder = System.IO.Path.GetDirectoryName(openFileDialog1.FileName);
                IniFile iniFile = new IniFile(this.openFileDialog1.FileName);
                string s = iniFile.IniReadValue("Settings", "NumOfScenarios");
                if (!Int32.TryParse(s, out this.NumOfScenarios) || this.NumOfScenarios < 1)
                {
                    MessageBox.Show(this, "There must be at least 1 scenario input!\nPlease check your input in setting.ini.");
                    return;
                }

                s = iniFile.IniReadValue("Settings", "ScenarioFileNames");
                string[] subString = s.Split(',');

                if (subString.Length != this.NumOfScenarios)
                {
                    MessageBox.Show(this, "The number of scenarios does not match the number of scenario names!\nPlease check the input in setting.ini.");
                    return;
                }
                else
                {
                    this.ScenariosFileNames = new String[this.NumOfScenarios];
                    for (int i = 0; i < this.NumOfScenarios; i++)
                    {
                        this.ScenariosFileNames[i] = this.projectFolder + @"\" + subString[i];
                        if (!File.Exists(this.ScenariosFileNames[i]))
                        {
                            MessageBox.Show(this, String.Format("Scenario file \"{0}\" does not exist under the project folder!\nPlease check the input in setting.ini.", subString[i]));
                            return;
                        }
                    }
                }

                s = iniFile.IniReadValue("Settings", "ScenarioTypes");
                subString = s.Split(',');

                if (subString.Length != this.NumOfScenarios)
                {
                    MessageBox.Show(this, "The number of scenarios does not match the number of scenario types!\nPlease check the input in setting.ini.");
                    return;
                }
                else
                {
                    this.ScenarioTypeArray = new VehicleTrajectoryType[this.NumOfScenarios];
                    for (int i = 0; i < subString.Length; i++)
                    {
                        if (subString[i].ToUpper().IndexOf("DYNASMART") != -1)
                        {
                            this.ScenarioTypeArray[i] = VehicleTrajectoryType.DYNASMART;
                        }
                        else
                        {
                            if (subString[i].ToUpper().IndexOf("AIMSUN") != -1)
                            {
                                this.ScenarioTypeArray[i] = VehicleTrajectoryType.AIMSUN;
                            }
                            else
                            {
                                MessageBox.Show(this, "Scenario type " + subString[i] + " is not supported!\nPlease check the input in setting.ini.");
                                return;
                            }                           
                        }
                    }
                }

                s = iniFile.IniReadValue("Settings", "ScenarioWeights");
                if (s.Trim().Length > 0)
                {
                    string[] subWeightsString = s.Split(',');
                    if (subWeightsString.Length != this.NumOfScenarios)
                    {
                        ScenarioWeights = new double[this.NumOfScenarios];
                        for (int i = 0; i < this.NumOfScenarios; i++)
                        {
                            ScenarioWeights[i] = 1 / this.NumOfScenarios;
                        }

                        if (ScenarioWeights.Sum() > 1)
                        {
                            MessageBox.Show(this, "The sum of the scenario weigths must add up to 1!\nPlease check your input in setting.ini.");
                            return;
                        }
                    }
                    else
                    {
                        ScenarioWeights = new double[this.NumOfScenarios];
                        for (int i = 0; i < this.NumOfScenarios; i++)
                        {
                            ScenarioWeights[i] = double.Parse(subWeightsString[i]);
                        }
                    }
                }
                else
                {
                    ScenarioWeights = new double[this.NumOfScenarios];
                    for (int i = 0; i < this.NumOfScenarios; i++)
                    {
                        ScenarioWeights[i] = 1.0 / this.NumOfScenarios;
                    }
                }
                

                s = iniFile.IniReadValue("Settings", "Analysis_Start_Hour");
                if (!Int32.TryParse(s, out Analysis_Start_Time))
                {
                    Analysis_Start_Time = 6;
                }
                s = iniFile.IniReadValue("Settings", "Analysis_End_Hour");
                if (!Int32.TryParse(s, out Analysis_End_Time))
                {
                    Analysis_End_Time = 12;
                }

                TimeIntervalSelectionForm timeIntervalSelectionForm = new TimeIntervalSelectionForm();
                //timeIntervalSelectionForm.Parent = this;
                timeIntervalSelectionForm.StartPosition = FormStartPosition.CenterParent;

                if (timeIntervalSelectionForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    Analysis_Start_Time = timeIntervalSelectionForm.selectedStartHour;
                    Analysis_End_Time = timeIntervalSelectionForm.selectedEndHour;
                }

                s = iniFile.IniReadValue("Settings","LoadGPSTrajectory");
                int value = Int32.Parse(s);
                this.bReadGPSTrajectory = (value == 1)?true:false;
                if (this.bReadGPSTrajectory)
                {
                    s = iniFile.IniReadValue("Settings","GPSTrajectoryFileName");
                    this.GPSTrajectoryFileName = this.projectFolder + @"\" + s;
                    if (!File.Exists(this.GPSTrajectoryFileName))
                    {
                        MessageBox.Show(this, String.Format("GPS trajectory file \"{0}\" does not exist under the project folder!\nPlease check the input in setting.ini.", s));
                        return;
                    }
                }

                s = iniFile.IniReadValue("Settings", "LoadNetworkKMLFile");
                value = Int32.Parse(s);
                this.bLoadNetworkKMLFile = (value == 1) ? true : false;
                if (this.bLoadNetworkKMLFile)
                {
                    s = iniFile.IniReadValue("Settings", "NetworkKMLFileName");
                    this.NetworkKMLFileName = this.projectFolder + @"\" + s;

                    if (!File.Exists(this.NetworkKMLFileName))
                    {
                        MessageBox.Show(this, String.Format("Network KML file \"{0}\" does not exist under the project folder!\nPlease check the input in setting.ini.", s));
                        return;
                    }

                    string srcFileName = NetworkKMLFileName;
                    string destFileName = System.IO.Path.Combine(Application.StartupPath, GEServer.RootDirectory, System.IO.Path.GetFileName(NetworkKMLFileName));
                    if (File.Exists(destFileName))
                    {
                        System.IO.File.Delete(destFileName);
                    }
                    System.IO.File.Copy(srcFileName, destFileName);

                    string kmlURL = String.Format("http://localhost:8080/{0}", System.IO.Path.GetFileName(srcFileName));

                    this.geWebBrowser1.FetchKml(kmlURL);
                }


                string nodeFileName = this.projectFolder + "\\input_node.csv";
                if (File.Exists(nodeFileName))
                {
                    nodeMap = VehicleTrajectoryApp.Node.ReadNodesFromFile(nodeFileName);

                    TAZZoneIDToNodeIDListMap = new Dictionary<int, List<int>>();
                    foreach (KeyValuePair<int, Node> kvp in nodeMap)
                    {
                        if (!TAZZoneIDToNodeIDListMap.ContainsKey(kvp.Value.TAZ_ID))
                        {
                            TAZZoneIDToNodeIDListMap[kvp.Value.TAZ_ID] = new List<int>();
                        }

                        TAZZoneIDToNodeIDListMap[kvp.Value.TAZ_ID].Add(kvp.Key);
                    }
                }
                else
                {
                    nodeMap = null;
                }

                Cursor = Cursors.WaitCursor;

                int[,] loadedVehicleByScenarios = new int[this.NumOfScenarios, Analysis_End_Time - Analysis_Start_Time];

                Dictionary<ulong, int[]> ODCountsSummary = new Dictionary<ulong, int[]>();

                for (int i = 0; i < this.NumOfScenarios; i++)
                {
                    if (this.ScenarioTypeArray[i] == VehicleTrajectoryType.DYNASMART)
                    {
                        vehicleList = DYNASMARTVehicle.ReadVehicleFile(this.ScenariosFileNames[i], Analysis_Start_Time, Analysis_End_Time);
                        if (vehicleList != null)
                        {
                            for (int n = 0; n < vehicleList.Count; n++)
                            {
                                DYNASMARTVehicle v = vehicleList[n];

                                if (v.OriginZoneID != v.DestinationZoneID)
                                {
                                    ulong key = ((ulong)v.OriginZoneID << 32) | ((uint)v.DestinationZoneID);
                                    if (!criticalODMap.ContainsKey(key))
                                    {
                                        criticalODMap[key] = new CriticalOD(this.NumOfScenarios);
                                    }

                                    if (!ODCountsSummary.ContainsKey(key))
                                    {
                                        ODCountsSummary[key] = new int[(this.NumOfScenarios + 1) * (Analysis_End_Time - Analysis_Start_Time)];
                                    }

                                    ODCountsSummary[key][i * (Analysis_End_Time - Analysis_Start_Time) + (v.VehicleStartHour - Analysis_Start_Time)]++;

                                    int departure_hour = v.VehicleStartHour;
                                    loadedVehicleByScenarios[i, departure_hour - Analysis_Start_Time]++;
                                    //if (departure_hour > 5)
                                    //{
                                    //    departure_hour = 5;
                                    //}
                                    List<int> nodeList = new List<int>();
                                    for (int nodeIdx = 0; nodeIdx < v.NodeList.Count; nodeIdx++)
                                    {
                                        nodeList.Add(v.linkArray[nodeIdx]);
                                    }

                                    criticalODMap[key].AddPath(departure_hour, i, v.VehicleTravelTime, new VehicleTrajectoryApp.Path(VehicleTrajectoryType.DYNASMART, nodeList, v.Travel_Distance_In_Mile));
                                }
                            }
                        }
                        vehicleList = null;
                    }
                    else
                    {
                        if (this.ScenarioTypeArray[i] == VehicleTrajectoryType.AIMSUN)
                        {
                            List<IVehicle> aimsunVehicleList = GPSTrajectory.ReadBinaryGPSTrajectory2(this.ScenariosFileNames[i], VehicleTrajectoryType.AIMSUN, Analysis_Start_Time, Analysis_End_Time);
                            foreach (IVehicle v in aimsunVehicleList)
                            {
                                if (v.OriginZoneID != v.DestinationZoneID)
                                {
                                    ulong key = ((ulong)v.OriginZoneID << 32) | ((uint)v.DestinationZoneID);
                                    if (!criticalODMap.ContainsKey(key))
                                    {
                                        criticalODMap[key] = new CriticalOD(this.NumOfScenarios);
                                    }

                                    if (!ODCountsSummary.ContainsKey(key))
                                    {
                                        ODCountsSummary[key] = new int[(this.NumOfScenarios + 1) * (Analysis_End_Time - Analysis_Start_Time)];
                                    }

                                    ODCountsSummary[key][i * (Analysis_End_Time - Analysis_Start_Time) + (v.VehicleStartHour - Analysis_Start_Time)]++;

                                    int departure_hour = v.VehicleStartHour;
                                    loadedVehicleByScenarios[i, departure_hour - Analysis_Start_Time]++;

                                    criticalODMap[key].AddPath(departure_hour, i, v.VehicleTravelTime, new VehicleTrajectoryApp.Path(VehicleTrajectoryType.AIMSUN, v.GetGPSLinkArray(), v.Travel_Distance_In_Mile));
                                }
                            }
                        }
                    }

                }

                int[] loadedGPSVehicleByHour = new int[Analysis_End_Time - Analysis_Start_Time];
                if (this.bReadGPSTrajectory)
                {
                    gpsTrajectoryList = GPSTrajectory.ReadBinaryGPSTrajectory2(this.GPSTrajectoryFileName, VehicleTrajectoryType.GPS, Analysis_Start_Time, Analysis_End_Time);
                    if (gpsTrajectoryList.Count > 0)
                    {
                        if (gpsVehicles == null)
                        {
                            gpsVehicles = new Dictionary<ulong, Dictionary<int, List<IVehicle>>>();
                        }

                        foreach (IVehicle v in gpsTrajectoryList)
                        {                            
                            ulong key = ((ulong)v.OriginZoneID << 32) | ((uint)v.DestinationZoneID);
                            if (!gpsVehicles.ContainsKey(key))
                            {
                                gpsVehicles[key] = new Dictionary<int, List<IVehicle>>();
                            }

                            if (!ODCountsSummary.ContainsKey(key))
                            {
                                ODCountsSummary[key] = new int[(this.NumOfScenarios + 1) * (Analysis_End_Time - Analysis_Start_Time)];
                            }

                            ODCountsSummary[key][this.NumOfScenarios * (Analysis_End_Time - Analysis_Start_Time) + (v.VehicleStartHour - Analysis_Start_Time)]++;

                            if (!gpsVehicles[key].ContainsKey(v.VehicleStartHour))
                            {
                                gpsVehicles[key][v.VehicleStartHour] = new List<IVehicle>();
                            }
                            loadedGPSVehicleByHour[v.VehicleStartHour - Analysis_Start_Time]++;

                            gpsVehicles[key][v.VehicleStartHour].Add(v);                            
                        }
                    }
                }

                string summaryText = string.Empty;
                int total;
                for (int i=0;i<this.NumOfScenarios;i++)
                {
                    summaryText += String.Format("Scenario {0}: {1}\n", i, this.ScenarioTypeArray[i].ToString());
                    total = 0;

                    for (int j = Analysis_Start_Time; j < Analysis_End_Time; j++)
                    {
                        summaryText += String.Format("{0} vehicles loaded between hours {1} and {2}.\n", loadedVehicleByScenarios[i, j - Analysis_Start_Time], j, j + 1);
                        total += loadedVehicleByScenarios[i, j - Analysis_Start_Time];
                    }
                    summaryText += String.Format("A total of {0} vehicles loaded.\n\n", total);
                }

                summaryText += "GPS:\n";
                total = 0;
                for (int j = Analysis_Start_Time; j < Analysis_End_Time; j++)
                {
                    summaryText += String.Format("{0} vehicles loaded between hours {1} and {2}.\n",loadedGPSVehicleByHour[j - Analysis_Start_Time], j, j+1);
                    total += loadedGPSVehicleByHour[j - Analysis_Start_Time];
                }
                summaryText += String.Format("A total of {0} vehicles loaded.\n", total);

                using (StreamWriter writer = new StreamWriter(this.projectFolder + "\\Vehicle_Loading_Summary.csv"))
                {
                    writer.Write(",,");
                    for (int i = 0; i < this.NumOfScenarios; i++)
                    {
                        writer.Write(this.ScenarioTypeArray[i] + ",,,");
                    }
                    if (bReadGPSTrajectory)
                    {
                        writer.Write("GPS");
                    }
                    writer.WriteLine();

                    writer.Write("Origin_Zone_ID,Dest_Zone_ID,");
                    for (int i = 0; i < this.NumOfScenarios; i++)
                    {
                        for (int j = Analysis_Start_Time; j < Analysis_End_Time; j++)
                        {
                            writer.Write(j + ",");
                        }
                    }

                    if (bReadGPSTrajectory)
                    {
                        for (int j = Analysis_Start_Time; j < Analysis_End_Time; j++)
                        {
                            writer.Write(j + ",");
                        }
                    }
                    writer.WriteLine();

                    foreach (KeyValuePair<ulong, int[]> kvp in ODCountsSummary)
                    {
                        ulong key = kvp.Key;
                        int OriginZone = (int)((key & 0xffffffff00000000) >> 32);
                        int DestZone = (int)(key & 0x00000000ffffffff);
                        writer.Write("{0},{1},", OriginZone, DestZone);
                        for (int i = 0; i < NumOfScenarios; i++)
                        {
                            for (int j = 0; j < (Analysis_End_Time - Analysis_Start_Time); j++)
                            {
                                writer.Write(kvp.Value[i * (Analysis_End_Time - Analysis_Start_Time) + j] + ",");
                            }
                        }
                        if (bReadGPSTrajectory)
                        {
                            for (int j = 0; j < (Analysis_End_Time - Analysis_Start_Time); j++)
                            {
                                writer.Write(kvp.Value[this.NumOfScenarios * (Analysis_End_Time - Analysis_Start_Time) + j] + ",");
                            }
                        }
                        writer.WriteLine();
                    }
                }

                MessageBox.Show(this, summaryText);

                this.toolStripButton8.Enabled = true;
                Cursor = Cursors.Default;
            }                             
        }


        private void toolStripButton5_Click(object sender, EventArgs e)
        {
            this.geWebBrowser1.FetchKml("http://localhost:8080/AMS_link_3D.kml");
        }

        private void toolStripButton6_Click(object sender, EventArgs e)
        {
            //if (gpsTrajectoryList == null)
            //{
            //    openFileDialog1.FileName = "";
            //    openFileDialog1.Filter = "Binary GPS Trajectory File (*.bin) | *.bin";
            //    if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            //    {
            //        gpsTrajectoryList = GPSTrajectory.ReadBinaryGPSTrajectory(openFileDialog1.FileName);
            //        MessageBox.Show(this, String.Format("{0} GPS trajectories read", gpsTrajectoryList.Count()));
            //    }
            //}

            //if (gpsTrajectoryList != null && gpsTrajectoryList.Count > 0)
            //{
            //    GPSTrajectoryAnalysisForm form = new GPSTrajectoryAnalysisForm();
            //    form.parentForm = this;
            //    form.gpsTrajectoryList = gpsTrajectoryList;
            //    form.Show();
            //}
            //else
            //{
            //    MessageBox.Show(this, "No GPS trajectory read!");
            //}
        }

        private void toolStripButton7_Click(object sender, EventArgs e)
        {
            if (criticalODMap.Keys.Count > 0)
            {
                ODTravelTimeStatistics form = new ODTravelTimeStatistics();
                form.criticalODMap = criticalODMap;
                form.parentForm = this;
                form.NumOfScenarios = this.NumOfScenarios;
                form.ScenarioNames = new string[this.NumOfScenarios];
                form.ScenarioWeights = this.ScenarioWeights;
                form.AnalysisStartTime = this.Analysis_Start_Time;
                form.AnalysisEndTime = this.Analysis_End_Time;
                form.gpsTrajectoryList = this.gpsTrajectoryList;
                form.gpsVehicles = this.gpsVehicles;
                form.DYNASMARTZoneToBPMZoneIDMap = this.DYNASMARTZoneIDToBPMZoneID;
                for (int n = 0; n < this.NumOfScenarios; n++)
                {
                    form.ScenarioNames[n] = System.IO.Path.GetFileNameWithoutExtension(this.ScenariosFileNames[n]);
                }
                form.Show();
            }
            else
            {
                MessageBox.Show(this, "No vehicle trajectory has been loaded!");
            }
        }

        public void AddPathOnMap(List<string> kmlStringList)
        {
            if (previousKMLObjList != null && previousKMLObjList.Count > 0)
            {
                for (int i = 0; i < previousKMLObjList.Count; i++)
                {
                    ge.getFeatures().removeChild(previousKMLObjList[i]);
                    previousKMLObjList[i] = null;
                }

                previousKMLObjList.Clear();
            }
            for (int i = 0; i < kmlStringList.Count; i++)
            {
                dynamic kmlObj = this.geWebBrowser1.ParseKml(kmlStringList[i]);

                if (kmlObj != null)
                {
                    geWebBrowser1.ParseKmlObject(kmlObj);
                    if (previousKMLObjList == null)
                    {
                        previousKMLObjList = new List<dynamic>();
                    }

                    //this.KmlTreeView1.ParseKmlObject(kmlObj);

                    previousKMLObjList.Add(kmlObj);
                }
            }
        }

        public void RemoveKmlObj()
        {
            if (previousKMLObjList != null && previousKMLObjList.Count > 0)
            {
                for (int i = 0; i < previousKMLObjList.Count; i++)
                {
                    ge.getFeatures().removeChild(previousKMLObjList[i]);
                    previousKMLObjList[i] = null;
                }

                previousKMLObjList.Clear();
            }
        }

        private void toolStripButton8_Click(object sender, EventArgs e)
        {
            SelectOriginZone = true;
            this.toolStripButton9.Enabled = true;

            if (ODKMLObj != null)
            {
                ge.getFeatures().removeChild(ODKMLObj);
                ODKMLObj = null;
            }

            if (StartPointKMLObj != null)
            {
                ge.getFeatures().removeChild(StartPointKMLObj);
                StartPointKMLObj = null;
            }

            if (EndPointKMLObj != null)
            {
                ge.getFeatures().removeChild(EndPointKMLObj);
                EndPointKMLObj = null;
            }
        }

        private void toolStripButton9_Click(object sender, EventArgs e)
        {
            SelectDestZone = true;
        }

        private void geToolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {

        }
    }

    public class GETraceListener : TraceListener
    {
        private RichTextBox output;

        public GETraceListener(RichTextBox output)
        {
            this.Name = "Trace";
            this.output = output;
        }

        public override void Write(string message)
        {
            Action append = delegate()
            {
                output.SelectionColor = System.Drawing.Color.DarkGray;
                output.AppendText(string.Format("[{0}] ", DateTime.Now.ToString()));

                if (message.Contains("error") || message.Contains("Error"))
                {
                    output.SelectionColor = System.Drawing.Color.DarkRed;
                }
                else
                {
                    output.SelectionColor = System.Drawing.Color.DarkBlue;
                }

                output.AppendText(message);
                output.SelectionStart = output.Text.Length;
                output.ScrollToCaret();
            };

            if (output.InvokeRequired)
            {
                output.BeginInvoke(append);
            }
            else
            {
                append();
            }
        }

        public override void WriteLine(string message)
        {
            Write(message + Environment.NewLine);
        }
    }

    public class IniFile
    {
        public string path;

        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section,
            string key, string val, string filePath);
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section,
                 string key, string def, StringBuilder retVal,
            int size, string filePath);

        /// <summary>
        /// INIFile Constructor.
        /// </summary>
        /// <PARAM name="INIPath"></PARAM>
        public IniFile(string INIPath)
        {
            path = INIPath;
        }
        /// <summary>
        /// Write Data to the INI File
        /// </summary>
        /// <PARAM name="Section"></PARAM>
        /// Section name
        /// <PARAM name="Key"></PARAM>
        /// Key Name
        /// <PARAM name="Value"></PARAM>
        /// Value Name
        public void IniWriteValue(string Section, string Key, string Value)
        {
            WritePrivateProfileString(Section, Key, Value, this.path);
        }

        /// <summary>
        /// Read Data Value From the Ini File
        /// </summary>
        /// <PARAM name="Section"></PARAM>
        /// <PARAM name="Key"></PARAM>
        /// <PARAM name="Path"></PARAM>
        /// <returns></returns>
        public string IniReadValue(string Section, string Key)
        {
            StringBuilder temp = new StringBuilder(255);
            int i = GetPrivateProfileString(Section, Key, "", temp, 255, this.path);
            return temp.ToString();
        }
    }
}